package com.chinafree.permission.service.impl;


import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.chinafree.common.model.exception.HttpMethodEnum;
import com.chinafree.mapper.PathMapper;
import com.chinafree.mapper.PermissionMapper;
import com.chinafree.permission.model.enumeration.ColumnEnum;
import com.chinafree.permission.model.enumeration.PermissionTypeEnum;
import com.chinafree.permission.model.enumeration.RedisKeyEnum;
import com.chinafree.permission.model.po.PathPO;
import com.chinafree.permission.model.po.PermissionPO;
import com.chinafree.permission.service.PathServie;
import com.chinafree.permission.service.RolePermissionService;
import com.google.common.collect.Sets;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections.CollectionUtils;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
import org.springframework.stereotype.Service;
import org.springframework.util.StringUtils;

import javax.annotation.Resource;
import java.util.*;
import java.util.concurrent.TimeUnit;

/**
 * @description: TODO
 * @author: longzix
 * @modified By: longzix
 * @date: Created in 2021/7/27 17:01
 * @version:v1.0
 */
@Service
@Slf4j
public class PathServiceImpl implements PathServie {

    @Resource
    private PathMapper pathMapper;

    @Resource
    private RedisTemplate redisTemplate;

    @Resource
    private PermissionMapper permissionMapper;
    @Resource
    private RolePermissionService rolePermissionService;
    @Resource
    private ThreadPoolTaskExecutor taskExecutor;

    /* *
     * @description: 判断某个用户是否拥有某个路径的权限
     * @param userId
     * @param path
     * @author longzix
     * @date 2021/7/27 20:04
     * @return java.lang.Boolean
     */
    @Override
    public boolean hasPermission(String userId, String path, String method) {
        //去DB查找，如果有此路径，则需要将其缓存，如果db没有，直接返回TRUE
        Set<Integer> pathPermissions = (Set<Integer>) redisTemplate.opsForValue().get(buildKeyOfPathPermission(path, method));
        if (CollectionUtils.isEmpty(pathPermissions)) {//两种情况，1，此路径没有分配权限，2.没有缓存
            saveAllPathTORedis();//缓存所有路径对应的权限
            pathPermissions = (Set<Integer>) redisTemplate.opsForValue().get(buildKeyOfPathPermission(path, method));
            if (CollectionUtils.isEmpty(pathPermissions)) {
                log.info("数据库没有配置此路径信息：{}", path);
                return true;
            }
        }
        Optional<Set<Integer>> optional = rolePermissionService.getUserPermissionList(userId);
        if (!optional.isPresent()) {
            return false;
        }
        Set<Integer> userPermissions = optional.get();
        log.info("请求路径：{}，路径权限：{}", path, pathPermissions);
        log.info("用户id：{},用户权限：{}", userId, userPermissions);
        Collection<Integer> intersection = CollectionUtils.intersection(userPermissions, pathPermissions);
        return !CollectionUtils.isEmpty(intersection);
    }

    /* *
     * @description: 将路径对应的权限 缓存到redis中
     * @author longzix
     * @date 2021/7/27 21:17
     * @return java.lang.Boolean
     */
    @Override
    public void saveAllPathTORedis() {
        Optional<Map<String, Set<Integer>>> optionalMap = getAllPathWithPermission();//从DB获取
        if (!optionalMap.isPresent()) {
            log.info("数据库中没有路径信息");
            return;
        }
        Map<String, Set<Integer>> stringListMap = optionalMap.get();
        Set<String> keySet = stringListMap.keySet();
//        log. info("缓存所有路径对应的权限：{}", stringListMap);
        redisTemplate.opsForValue().multiSet(stringListMap);
        taskExecutor.execute(() -> {
            keySet.forEach(e -> {
                redisTemplate.expire(e, 1, TimeUnit.DAYS);//异步设置过期时间 一天
            });
        });
    }

    /* *
     * @description: 将权限封装到对应的请求路径下
     * @author longzix
     * @date 2021/7/27 17:41
     * @return java.util.Optional<java.util.Map<java.lang.String,java.util.Set<java.lang.Integer>>>
     */
    @Override
    public Optional<Map<String, Set<Integer>>> getAllPathWithPermission() {
        QueryWrapper<PathPO> wrapper = new QueryWrapper<>();
        wrapper.eq(ColumnEnum.IS_DELETE.getValue(),0);
        List<PathPO> pathPOS = pathMapper.selectList(wrapper);//获取所有路径
        if (CollectionUtils.isEmpty(pathPOS)) {
            return Optional.empty();
        }
        Map<String, Set<Integer>> map = new HashMap<>();//将权限封装到路径下面, //key 为 path的值，values 为对应的权限的 id的集合
        for (PathPO pathPO : pathPOS) {
            String pathMethod = getPathMethod(pathPO.getMethod());
            String path = pathPO.getPath();
            if (StringUtils.isEmpty(pathMethod)) {
                log.error("枚举方法未找到！method:{},path:{}", pathPO.getMethod(), path);
                return Optional.empty();
            }
            Integer permissionId = pathPO.getPermissionId();
            Set<Integer> permissionList = map.computeIfAbsent(buildKeyOfPathPermission(path, pathMethod), k -> Sets.newHashSet());
            permissionList.add(permissionId);
        }
        return Optional.of(map);
    }

    private String getPathMethod(Integer method) {
        HttpMethodEnum[] values = HttpMethodEnum.values();
        for (HttpMethodEnum methodEnum : values) {
            if (methodEnum.getId().equals(method)) {
                return Integer.toString(methodEnum.getValue());
            }
        }
        return null;
    }

    @Override
    public String getPathInfo(String uid, String path, String method) {
        StringBuilder stringBuilder = new StringBuilder();
        Set<Integer> pathSet = (Set<Integer>) redisTemplate.opsForValue().get(buildKeyOfPathPermission(path, method));//路径对应的权限
        Set<Integer> userSet = (Set<Integer>) redisTemplate.opsForValue().get(RedisKeyEnum.BOSS_UID.getValue() + uid);//用户对应的权限
        if (userSet == null) {
            userSet = new HashSet<>();
        }
        List<Integer> subtract = (List<Integer>) CollectionUtils.subtract(pathSet, userSet);
        if (CollectionUtils.isEmpty(subtract)) {
            return stringBuilder.toString();
        }
        List<PermissionPO> permissionPOS = permissionMapper.selectPermissionListByPermissionIdList(subtract);
        permissionPOS.forEach(p -> {
            Integer type = p.getType();
            PermissionTypeEnum[] values = PermissionTypeEnum.values();
            for (PermissionTypeEnum value : values) {
                if (value.getTypeId().equals(type)) {
                    stringBuilder.append("「 ").append(value.getTypeName()).append(":");
                    break;
                }
            }
            stringBuilder.append(p.getName()).append(" 」");
        });
        return stringBuilder.toString();
    }

    private String buildKeyOfPathPermission(String path, String method) {
        return RedisKeyEnum.BOSS_PATH.getValue() + method.toUpperCase() + ":" + path;
    }
}
